//
//  Clock.h
//  sad
//
//  Created by Christopher Kemsley on 1/31/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//


#include "PubSub.hpp"
#include "ModuleFactory.hpp"

#include "Settings.hpp"
#include "Codec.hpp"

#include "Log.hpp"
#include <iostream>


struct ClockSettings : public Settings
{
	SCInterval	halfPeriod ;
	
	std::string	clockName ;
	std::string	resetName ;
	
	
	ClockSettings ( std::string const & name )
	:	Settings (name)
	,	halfPeriod	( 1 , kSCIntervalMiliSeconds )
	,	clockName	(  )
	,	resetName	(  )
		{
		add ( "period" , halfPeriod ) ;
		add ( "clockSignal" , clockName ) ;
		add ( "disableSignal" , resetName ) ;
		}
	
	
	virtual void		validate ()
		{
		if ( clockName.size() == 0 )
			ThrowException ( "ClockSettings" , "Clock output name wasn't set" , __FILE__,__LINE__ ) ;
		halfPeriod /= 2 ;
		}
} ;







class	Clock : public ConfiguredModel<ClockSettings>
{
public:
	
	SC_HAS_PROCESS(Clock) ;
	
	
	Clock	( ModelVariant const & identity )
	:	ConfiguredModel<ClockSettings>		( identity )
	,	mOutput		( )
	,	mDisable	( 0 )
		{
		Log::states("Clock") << "Clock\n	half-period is " << mConfig.halfPeriod.to_seconds() ;
		
		mOutput		=	mConfig.clockName ;
		if ( mConfig.resetName.size() != 0 )
			{
			mDisable	=	new SCInputSignal<SCLogic> (  ) ;
			*mDisable	=	mConfig.resetName ;
			}
		
		
		SC_THREAD ( runLoop ) ;
		}
	virtual ~Clock	()
		{
		
		}
	
	
	
private:
	SCOutputSignal<bool>	mOutput ;
	SCInputSignal<SCLogic>*	mDisable ;
	
	
	void	runLoop ()
		{
		if ( mDisable != 0 )
			{
			while ( 1 )
				{
				if ( kSCLogicTrue == **mDisable )
					{
					Log::states("Clock") << "waiting for reset sink" ;
					wait ( (*mDisable)->negedge_event() ) ;
					Log::states("Clock") << "reset did sink" ;
					}
				
				
				mOutput << true ;
				
				wait ( mConfig.halfPeriod , (*mDisable)->posedge_event() ) ;
				
				if ( kSCLogicTrue == *(*mDisable) )
					Log::states("Clock") << "wait ended early for reset" ;
				
				mOutput << false ;
				
				wait ( mConfig.halfPeriod , (*mDisable)->posedge_event() ) ;
				
				if ( kSCLogicTrue == *(*mDisable) )
					Log::states("Clock") << "wait ended early for reset" ;
				}
			}
		else
			{
			while ( 1 )
				{
				mOutput << true ;
				
				wait ( mConfig.halfPeriod ) ;
				
				mOutput << false ;
				
				wait ( mConfig.halfPeriod ) ;
				}
			}
		}
} ;



TypedModuleMaker<Clock>	_Clock ( "Clock.boolSignal" ) ;

